package xj.graph2d.shapes;

import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Map;

import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeBase;
import xj.graph2d.GraphNodeBase;
import xj.graph2d.SelfLoop;

public class PolyCurveSelfLoop 
  extends PolyCurveShape 
  implements SelfLoop, ShapeConstants {

  public PolyCurveSelfLoop(float x1, float y1, float x2, float y2, 
			   float width, float height, int direction) {
    this(x1, y1, x2, y2, width, height, direction, false);
  }

  public PolyCurveSelfLoop(float x1, float y1, float x2, float y2, 
			   float width, float height, int direction, 
			   boolean createComp) {
    super(x1, y1, createComp);
    allowSelfLoop = true;
    this.height = height;
    this.width = width;
    this.direction = direction;
    float x = (x1 + x2) / 2;
    float y = (y1 + y2) / 2;

    // boolean inverse = (direction & INVERSE_DIRECTION) != 0;
    boolean inverse = (direction >= INVERSE_DIRECTION);
    switch (direction) {
    case NORTH_EAST:
    case NORTH_EAST + INVERSE_DIRECTION:
      x = Math.max(x1, x2) + width;
      y = Math.min(y1, y2) - height;
      break;
    case NORTH_WEST:
    case NORTH_WEST + INVERSE_DIRECTION:
      x = Math.min(x1, x2) - width;
      y = Math.min(y1, y2) - height;
      break;
    case SOUTH_EAST:
    case SOUTH_EAST + INVERSE_DIRECTION:
      x = Math.max(x1, x2) + width;
      y = Math.max(y1, y2) + height;
      break;
    case SOUTH_WEST:
    case SOUTH_WEST + INVERSE_DIRECTION:
      x = Math.min(x1, x2) - width;
      y = Math.max(y1, y2) + height;
      break;
    }
    float cx1, cy1, cx2, cy2;
    if (inverse) {
      cx1 = x;
      cy1 = y1;
      cx2 = x2;
      cy2 = y;
    } else {
      cx1 = x1;
      cy1 = y;
      cx2 = x;
      cy2 = y2;
    }

    addPoint(x, y);
    addPoint(x2, y2);
    setCtr1(cx1, cy1);
    setCtr2(cx2, cy2);
  }

  public PolyCurveSelfLoop(boolean createComp) {
    this(20, 20, NORTH, createComp);
  }

  public PolyCurveSelfLoop(float width, float height, int direction, boolean createComp) {
    super(createComp);
    allowSelfLoop = true;
    this.height = height;
    this.width = width;
    this.direction = direction;
  }

  @Override
  public DrawShape makeInstance() {
    return new PolyCurveSelfLoop(width, height, direction, comp != null);
  }

  @Override
  public void copyTo(DrawShapeBase s) 
    throws CloneNotSupportedException {
    if (s != null) {
      super.copyTo(s);
      if (s instanceof PolyCurveSelfLoop) {
	PolyCurveSelfLoop r = (PolyCurveSelfLoop) s;
	r.height = height;
	r.width = width;
	r.direction = direction;
      }
    }
  }

  public boolean isOrthogonal() {
    return false; 
  }

  public boolean isOneSided() {
    return false; 
  }

  public int getDirection() {
    return direction;
  }

  public void setDirection(int direction) {
    this.direction = direction;
    // positionLabel();
    // adjustContent();
  }

  public float getHeight() {
    return height;
  }

  public void setHeight(float height) {
    this.height = height;
  }

  public float getWidth() {
    return width;
  }

  public void setWidth(float width) {
    this.width = width;
  }

  public boolean isAdjustable() {
    return adjustable;
  }

  public void setAdjustable(boolean b) {
    adjustable = b;
  }

  @Override
  public void drawOutline(Graphics2D g2, float x1, float y1, float x2, float y2) {
    adjustSelfLoop();
    drawShape(g2);
  }

  public void adjustDirection(Rectangle2D bounds, float x1, float y1, float x2,
      float y2) {
    boolean inverse = false;
    if (bounds != null) {
      float nx = (float) bounds.getX();
      float ny = (float) bounds.getY();
      float nw = (float) bounds.getWidth();
      float nh = (float) bounds.getHeight();
      float div = nx + nw / 2;
      float cx = (x1 + x2) / 2;
      if (x1 < x2) {
	if (cx < div) {
	  inverse = true;
	}
      } else {
	if (cx >= div) {
	  inverse = true;
	}
      }
    }

    if (y1 < y2) {
      if (x1 < x2) {
	direction = (inverse ? (SOUTH_WEST + INVERSE_DIRECTION) : NORTH_EAST);
      } else {
	direction = (inverse ? (SOUTH_EAST + INVERSE_DIRECTION) : NORTH_WEST);
      }
    } else {
      if (x1 < x2) {
	direction = (inverse ? (NORTH_WEST + INVERSE_DIRECTION) : SOUTH_EAST);
      } else {
	direction = (inverse ? (NORTH_EAST + INVERSE_DIRECTION) : SOUTH_WEST);
      }
    }
  }

  @Override
  public void adjustSelfLoop() {
    if (debug) {
      System.out.println("PolyCurveSelfLoop.adjustSelfLoop()");
    }

    float x1 = getX();
    float y1 = getY();
    float x2, y2;
    Point2D p = getEnd2();
    if (p != null) {
      x2 = (float) p.getX();
      y2 = (float) p.getY();

      float x = (x1 + x2) / 2;
      float y = (y1 + y2) / 2;
      boolean inverse = (direction >= INVERSE_DIRECTION);
      switch (direction) {
      case NORTH_EAST:
      case NORTH_EAST + INVERSE_DIRECTION:
	x = Math.max(x1, x2) + width;
	y = Math.min(y1, y2) - height;
	break;
      case NORTH_WEST:
      case NORTH_WEST + INVERSE_DIRECTION:
	x = Math.min(x1, x2) - width;
	y = Math.min(y1, y2) - height;
	break;
      case SOUTH_EAST:
      case SOUTH_EAST + INVERSE_DIRECTION:
	x = Math.max(x1, x2) + width;
	y = Math.max(y1, y2) + height;
	break;
      case SOUTH_WEST:
      case SOUTH_WEST + INVERSE_DIRECTION:
	x = Math.min(x1, x2) - width;
	y = Math.max(y1, y2) + height;
	break;
      }
      float cx1, cy1, cx2, cy2;
      if (inverse) {
	cx1 = x;
	cy1 = y1;
	cx2 = x2;
	cy2 = y;
      } else {
	cx1 = x1;
	cy1 = y;
	cx2 = x;
	cy2 = y2;
      }
      // addPoint(x, y);
      setPoint(1, x, y);
      setCtr1(cx1, cy1);
      setCtr2(cx2, cy2);

      adapter.invalidateBounds();
      calculateBounds();

      if (debug) {
	System.out.println("PolyCurveSelfLoop.adjustSelfLoop() " + 
			   " x1=" + x1 + " y1=" + y1 + " x2=" + x2 + " y2=" + y2 + 
			   " x=" + x + " y=" + y + 
			   " cx1=" + cx1 + " cy1=" + cy1 + " cx2=" + cx2 + " cy2=" + cy2);
      }
    }
  }

  @Override
public void setEndsAdj(float x1, float y1, float x2, float y2) {
    float xmin, ymin, xmax, ymax;
    xmin = (float) bounds.getX();
    ymin = (float) bounds.getY();
    xmax = (float) (bounds.getX() + bounds.getWidth());
    ymax = (float) (bounds.getY() + bounds.getHeight());
    float x0 = Math.min(x1, x2);
    float y0 = Math.min(y1, y2);
    float sx = 1;
    float sy = 1;
    if (x1 == x2 && y1 == y2) {
      sx = 0;
      sy = 0;
    } else {
      if (xmax != xmin) {
	sx = Math.abs(x2 - x1) / (xmax - xmin);
      }
      if (ymax != ymin) {
	sy = Math.abs(y2 - y1) / (ymax - ymin);
      }
    }
    width = width * sx; 
    height = height * sy; 

    super.setEndsAdj(x1, y1, x2, y2);
  }

  @Override
  public void adjustControlPoints() {
  }

  @Override
  public void adjustEnds(GraphNodeBase src, GraphNodeBase des, 
			 boolean adjustCtrPts) {
    super.adjustEnds(src, des, adjustCtrPts);
    if (!adjustable) { 
      adjustSelfLoop();
    }
  }

  @Override
  protected Map<String, Object> getXMLAttributes() {
    Map<String, Object> attrs = super.getXMLAttributes();
    if (attrs == null) {
      attrs = new HashMap<String, Object>();
    }
    attrs.put("height", height);
    attrs.put("width", width);
    attrs.put("direction", direction);
    return attrs;
  }

  @Override
  protected void processXMLAttributes(org.w3c.dom.Element node) {
    if (node != null) {
      super.processXMLAttributes(node);

      String s;
      s = node.getAttribute("height");
      try {
	height = Float.parseFloat(s);
      } catch (NumberFormatException ex) {
      }
      s = node.getAttribute("width");
      try {
	width = Float.parseFloat(s);
      } catch (NumberFormatException ex) {
      }
      s = node.getAttribute("direction");
      try {
	direction = Integer.parseInt(s);
      } catch (NumberFormatException ex) {
      }
    }
  }

  protected float height;
  protected float width;
  protected int direction;
  protected boolean adjustable = true;

  protected static final boolean debug = false;

}
